﻿#include "pch.h"
#include "sbcs.h"

#if _SB_WINDOWS_DESKTOP_

//////////////////////////////////////////////////////////////////////////
// 레지스트리
typedef LONG(WINAPI *type_RegOpenKeyExA)(HKEY, LPCSTR, DWORD, REGSAM, PHKEY);
typedef LONG(WINAPI *type_RegCloseKey)(HKEY);
typedef LONG(WINAPI *type_RegQueryValueExA)(HKEY, LPCSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD);
typedef LONG(WINAPI *type_RegEnumValueA)(HKEY, DWORD, LPSTR, LPDWORD, LPDWORD, LPDWORD, LPBYTE, LPDWORD);

static struct kWin32Registry
{
	HMODULE                 dll_advapi32;
	type_RegOpenKeyExA      func_RegOpenKeyExA;
	type_RegCloseKey        func_RegCloseKey;
	type_RegQueryValueExA   func_RegQueryValueExA;
	type_RegEnumValueA      func_RegEnumValueA;
} k_impl_regi =
{
	NULL,
};

static void _k_disp_registry(kpointer ptr)
{
	FreeLibrary(k_impl_regi.dll_advapi32);
}

static kcham _k_init_registry(void)
{
	if (k_impl_regi.dll_advapi32 != NULL)
		return TRUE;

	k_impl_regi.dll_advapi32 = LoadLibrary(L"advapi32");

	if (!k_impl_regi.dll_advapi32)
		return FALSE;

	k_impl_regi.func_RegOpenKeyExA = (type_RegOpenKeyExA)GetProcAddress(k_impl_regi.dll_advapi32, "RegOpenKeyExA");
	k_impl_regi.func_RegCloseKey = (type_RegCloseKey)GetProcAddress(k_impl_regi.dll_advapi32, "RegCloseKey");
	k_impl_regi.func_RegQueryValueExA = (type_RegQueryValueExA)GetProcAddress(k_impl_regi.dll_advapi32, "RegQueryValueExA");
	k_impl_regi.func_RegEnumValueA = (type_RegEnumValueA)GetProcAddress(k_impl_regi.dll_advapi32, "RegEnumValueA");

	k_atexit_p(_k_disp_registry, NULL);

	return TRUE;
}

// 해당키
static HKEY _k_regi_find_key(const char* key)
{
	// 파트1
	if (k_strieqv(key, "hkcr"))
		return HKEY_CLASSES_ROOT;

	if (k_strieqv(key, "hkcu"))
		return HKEY_CURRENT_USER;

	if (k_strieqv(key, "hklm"))
		return HKEY_LOCAL_MACHINE;

	if (k_strieqv(key, "hkus"))
		return HKEY_USERS;

	if (k_strieqv(key, "hkpd"))
		return HKEY_PERFORMANCE_DATA;

	if (k_strieqv(key, "hkpt"))
		return HKEY_PERFORMANCE_TEXT;

	if (k_strieqv(key, "hkpn"))
		return HKEY_PERFORMANCE_NLSTEXT;

#if WINVER>0x0400
	if (k_strieqv(key, "hkcc"))
		return HKEY_CURRENT_CONFIG;

	if (k_strieqv(key, "hkdd"))
		return HKEY_DYN_DATA;

	if (k_strieqv(key, "hkculs"))
		return HKEY_CURRENT_USER_LOCAL_SETTINGS;
#endif

	// 파트2
	if (k_strieqv(key, "CLASSES ROOT"))
		return HKEY_CLASSES_ROOT;

	if (k_strieqv(key, "CURRENT USER"))
		return HKEY_CURRENT_USER;

	if (k_strieqv(key, "LOCAL MACHINE"))
		return HKEY_LOCAL_MACHINE;

	if (k_strieqv(key, "USERS"))
		return HKEY_USERS;

	if (k_strieqv(key, "PERFORMANCE DATA"))
		return HKEY_PERFORMANCE_DATA;

	if (k_strieqv(key, "PERFORMANCE TEXT"))
		return HKEY_PERFORMANCE_TEXT;

	if (k_strieqv(key, "PERFORMANCE NLSTEXT"))
		return HKEY_PERFORMANCE_NLSTEXT;

#if WINVER>0x0400
	if (k_strieqv(key, "CURRENT CONFIG"))
		return HKEY_CURRENT_CONFIG;

	if (k_strieqv(key, "DYN DATA"))
		return HKEY_DYN_DATA;

	if (k_strieqv(key, "CURRENT USER LOCAL SETTINGS"))
		return HKEY_CURRENT_USER_LOCAL_SETTINGS;
#endif

	// 헉
	return NULL;
}

/**
 * 레지스트리를 연다.
 * @param	container	컨테이너.
 * @param	name	 	이름.
 * @param	canwrite 	쓸 수 있게 하려면 참으로 넣는다.
 * @return	문제가 있거나 실패하면 널값을 반환, 성공할 때 반환값은 kRegistry*.
 */
kRegistry* k_regi_new(const char* container, const char* name, kcham canwrite)
{
	DWORD access;
	HKEY ckey, key;

	k_return_value_if_fail(container != NULL, NULL);
	k_return_value_if_fail(name != NULL, NULL);

	if (!_k_init_registry())
		return NULL;

	if (!k_impl_regi.func_RegOpenKeyExA)
		return NULL;

	ckey = _k_regi_find_key(container);

	if (!ckey)
		return NULL;

	access = KEY_READ;

	if (canwrite)
		access |= KEY_WRITE;

	if (k_impl_regi.func_RegOpenKeyExA(ckey, name, 0, access, &key) != ERROR_SUCCESS)
		return NULL;

	return (kRegistry*)key;
}

/**
 * 레지스트리를 닫는다.
 * @param [입력] key   레지스트리 자기 자신 포인터.
 */
void k_regi_delete(kRegistry* key)
{
	if (!_k_init_registry())
		return;

	if (!k_impl_regi.func_RegCloseKey)
		return;

	k_impl_regi.func_RegCloseKey((HKEY)key);
}

/**
 * 레지스트리 내용을 열거한다.
 * @param [입력] key   레지스트리 자기 자신 포인터.
 * @param	index		  	0부터 시작하는 순번.
 * @param [반환]	buffer	버퍼 데이터.
 * @param	size		  	버퍼의 크기.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_regi_enum(kRegistry* key, int index, char* buffer, int size)
{
	DWORD type;
	LONG res;

	k_return_value_if_fail(index >= 0, FALSE);
	k_return_value_if_fail(buffer != NULL, FALSE);
	k_return_value_if_fail(size > 0, FALSE);

	if (!_k_init_registry())
		return FALSE;

	if (!k_impl_regi.func_RegEnumValueA)
		return FALSE;

	type = 0;
	res = k_impl_regi.func_RegEnumValueA((HKEY)key, (DWORD)index,
		buffer, (LPDWORD)&size, NULL, &type, NULL, NULL);

	if (res == ERROR_SUCCESS || res == ERROR_MORE_DATA)
		return TRUE;

	return FALSE;
}

/**
 * 레지스트리를 질의 한다.
 * @param [입력] key   레지스트리 자기 자신 포인터.
 * @param	name		   	이름.
 * @param [반환]	rettype	반환되는 타입.
 * @param [반환]	retsize	반환되는 크기.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_regi_query_type(kRegistry* key, const char* name, int* rettype, int* retsize)
{
	DWORD type, size;
	LONG res;

	k_return_value_if_fail(name != NULL, FALSE);

	if (!_k_init_registry())
		return FALSE;

	if (!k_impl_regi.func_RegQueryValueExA)
		return FALSE;

	type = 0;
	size = 0;
	res = k_impl_regi.func_RegQueryValueExA((HKEY)key, name, NULL, &type, NULL, &size);

	if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA)
		return FALSE;

	if (rettype)
		*rettype = (int)type;

	if (retsize)
		*retsize = (int)size;

	return TRUE;
}

/**
 * 레지스트리에서 임의의 알 수 없는 형식으로 얻는다.
 * @param [입력] key   레지스트리 자기 자신 포인터.
 * @param	name		   	이름.
 * @param [반환]	rettype	반환되는 타입.
 * @param [반환]	retsize	반환되는 크기.
 * @return	문제가 있거나 실패하면 널값을 반환, 성공할 때 반환값은 레지스트리 항목 데이터.
 */
kbyte* k_regi_get_unknown(kRegistry* key, const char* name, int* rettype, int* retsize)
{
	DWORD type, size;
	LONG res;
	kbyte* buffer;

	k_return_value_if_fail(name != NULL, FALSE);

	if (!_k_init_registry())
		return FALSE;

	if (!k_impl_regi.func_RegQueryValueExA)
		return FALSE;

	type = 0;
	size = 0;
	res = k_impl_regi.func_RegQueryValueExA((HKEY)key, name, NULL, &type, NULL, &size);

	if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA)
		return FALSE;

	buffer = k_new(size, kbyte);
	res = k_impl_regi.func_RegQueryValueExA((HKEY)key, name, NULL, &type, (LPBYTE)buffer, &size);

	if (res != ERROR_SUCCESS)
	{
		k_delete(buffer);
		return FALSE;
	}

	if (rettype)
		*rettype = (int)type;

	if (retsize)
		*retsize = (int)size;

	return buffer;
}

/**
 * 레지스트리에서 이진 데이터를 얻는다
 * @param [입력] key   레지스트리 자기 자신 포인터.
 * @param	name		   	이름.
 * @param [반환]	retsize	반환 크기.
 * @return	문제가 있거나 실패하면 널값을 반환, 성공할 때 반환값은 이진 데이터 버퍼.
 */
kbyte* k_regi_get_binary(kRegistry* key, const char* name, int* retsize)
{
	DWORD type, size;
	LONG res;
	kbyte* buffer;

	k_return_value_if_fail(name != NULL, FALSE);

	if (!_k_init_registry())
		return FALSE;

	if (!k_impl_regi.func_RegQueryValueExA)
		return FALSE;

	type = 0;
	size = 0;
	res = k_impl_regi.func_RegQueryValueExA((HKEY)key, name, NULL, &type, NULL, &size);

	if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA)
		return FALSE;

	if (type != REG_BINARY)
		return FALSE;

	buffer = k_new(size, kbyte);
	res = k_impl_regi.func_RegQueryValueExA((HKEY)key, name, NULL, &type, (LPBYTE)buffer, &size);

	if (res != ERROR_SUCCESS)
	{
		k_delete(buffer);
		return FALSE;
	}

	if (retsize)
		*retsize = (int)size;

	return buffer;
}

/**
 * 레지스트레이서 다중 문자열을 얻는다
 * @param [입력] key   레지스트리 자기 자신 포인터.
 * @param	name		   	이름.
 * @param [반환]	retsize	반환 크기.
 * @return	문제가 있거나 실패하면 널값을 반환, 성공할 때 반환값은 문자열.
 */
kchar* k_regi_get_multi_string(kRegistry* key, const char* name, int* retsize)
{
	DWORD type, size;
	LONG res;
	char* buffer;

	k_return_value_if_fail(name != NULL, FALSE);

	if (!_k_init_registry())
		return FALSE;

	if (!k_impl_regi.func_RegQueryValueExA)
		return FALSE;

	type = 0;
	size = 0;
	res = k_impl_regi.func_RegQueryValueExA((HKEY)key, name, NULL, &type, NULL, &size);

	if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA)
		return FALSE;

	if (type != REG_MULTI_SZ)
		return FALSE;

	buffer = k_new(size, char);
	res = k_impl_regi.func_RegQueryValueExA((HKEY)key, name, NULL, &type, (LPBYTE)buffer, &size);

	if (res != ERROR_SUCCESS)
	{
		k_delete(buffer);
		return FALSE;
	}

	if (retsize)
		*retsize = (int)size;

	return buffer;
}

/**
 * 레지스트리에서 문자열을 얻는다.
 * @param [입력,반환]	key   	(널값이 아니면) key.
 * @param	name		  	이름.
 * @param [반환]	buffer	반환 버퍼.
 * @param	size		  	반환 버퍼의 크기.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_regi_get_string(kRegistry* key, const char* name, char* buffer, int size)
{
	DWORD type, tmpsize;
	LONG res;

	k_return_value_if_fail(name != NULL, FALSE);
	k_return_value_if_fail(buffer != NULL, FALSE);
	k_return_value_if_fail(size > 0, FALSE);

	if (!_k_init_registry())
		return FALSE;

	if (!k_impl_regi.func_RegQueryValueExA)
		return FALSE;

	type = 0;
	tmpsize = 0;
	res = k_impl_regi.func_RegQueryValueExA((HKEY)key, name, NULL, &type, NULL, &tmpsize);

	if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA)
		return FALSE;

	if (type != REG_SZ && type != REG_EXPAND_SZ)
		return FALSE;

	if (tmpsize > (DWORD)size)
		tmpsize = (DWORD)size;

	res = k_impl_regi.func_RegQueryValueExA((HKEY)key, name, NULL, &type, (LPBYTE)buffer, &tmpsize);

	if (res != ERROR_SUCCESS)
		return FALSE;

	return TRUE;
}

/**
 * 레지스트리에서 정수를 얻는다
 * @param [입력] key   레지스트리 자기 자신 포인터.
 * @param	name		 	이름.
 * @param [반환]	value	반환 값.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_regi_get_int(kRegistry* key, const char* name, kint* value)
{
	DWORD type, size;
	LONG res;

	k_return_value_if_fail(name != NULL, FALSE);

	if (!_k_init_registry())
		return FALSE;

	if (!k_impl_regi.func_RegQueryValueExA)
		return FALSE;

	type = 0;
	size = 0;
	res = k_impl_regi.func_RegQueryValueExA((HKEY)key, name, NULL, &type, NULL, &size);

	if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA)
		return FALSE;

	if (type != REG_DWORD)
		return FALSE;

	res = k_impl_regi.func_RegQueryValueExA((HKEY)key, name, NULL, &type, (LPBYTE)value, &size);

	if (res != ERROR_SUCCESS)
		return FALSE;

	return TRUE;
}

/**
 * 레지스트리에서 64비트 정수를 얻는다.
 * @param [입력] key   레지스트리 자기 자신 포인터.
 * @param	name		 	이름.
 * @param [입력,반환]	value	반환 값.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_regi_get_long(kRegistry* key, const char* name, klong* value)
{
	DWORD type, size;
	LONG res;

	k_return_value_if_fail(name != NULL, FALSE);

	if (!_k_init_registry())
		return FALSE;

	if (!k_impl_regi.func_RegQueryValueExA)
		return FALSE;

	type = 0;
	size = 0;
	res = k_impl_regi.func_RegQueryValueExA((HKEY)key, name, NULL, &type, NULL, &size);

	if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA)
		return FALSE;

	if (type != REG_QWORD)
		return FALSE;

	res = k_impl_regi.func_RegQueryValueExA((HKEY)key, name, NULL, &type, (LPBYTE)value, &size);

	if (res != ERROR_SUCCESS)
		return FALSE;

	return TRUE;
}

#endif

